/*********************************************************
          File Name:event.c
          Author: Abby Cin
          Mail: abbytsing@gmail.com
          Created Time: Mon 27 Jul 2015 07:03:38 PM CST
**********************************************************/

#include "event.h"
#include "sock.h"
#include "lock.h"
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>


int assign_job(int n, struct task *job)
{
        int i;
        for(i = 0; i < n; i++)
        {
                pthread_rwlock_rdlock(&job[i].rw);
                if(job[i].done == 1)
                {
                        pthread_rwlock_unlock(&job[i].rw);
                        return i;
                }
                pthread_rwlock_unlock(&job[i].rw);
        }
        return -1;
}

static void assign_helper(int *idx, int n, struct task *job)
{
        for(;;)
        {
                if((*idx = assign_job(n, job)) != -1)
                        break;
                usleep(100000);
        }
}

static int ev_register(int efd, int fd)
{
        int res = 0;
        Event ev;
#ifdef __linux__
        memset(&ev, 0, sizeof(ev));
        ev.data.fd = fd;
        ev.events = EPOLLIN | EPOLLET;                 /* non-block I/O */
        res = epoll_ctl(efd, EPOLL_CTL_ADD, fd, &ev);

#elif (defined __FreeBSD__) || (defined __APPLE__)

        EV_SET(&ev, fd, EVFILT_READ, EV_ADD, 0, 0, NULL);
        res = kevent(efd, &ev, 1, NULL, 0, NULL);
#endif
        return res;
}

int ev_init(int fd)
{
        int efd = 0;
#ifdef __linux__
        efd = epoll_create(MAX_EVENTS);
#elif (defined __FreeBSD__) || (defined __APPLE__)
        efd = kqueue();
#endif
        if(efd == -1)
                return -1;
        if(ev_register(efd, fd) == -1)
                return -1;

        return efd;
}

#ifdef __linux__

int ev_run(int *breakall, int listenfd, int *efd, Event *events, 
              int n, struct task *job, cb inform_cb)
{
        int nfd, i, connfd, idx = 0;

        for(;*breakall == 0;)
        {
                nfd = epoll_wait(*efd, events, MAX_EVENTS, -1);

                for(i = 0; i < nfd; i++)
                {
                        assign_helper(&idx, n, job);

                        if((events[i].events & EPOLLERR) ||
                                (events[i].events & EPOLLHUP) ||
                                (!(events[i].events & EPOLLIN)))
                        {
                                close(events[i].data.fd);
                                continue;
                        }
                        else if(listenfd == events[i].data.fd)
                        {
                                while(1)
                                {
                                        acc_lock();
                                        connfd = accept(listenfd, NULL, NULL);
                                        acc_release();

                                        if(connfd == -1)
                                                break;
                                        if(nonblock_socket(connfd) == -1)
                                                perror("set_non_block()");
                                        ev_register(*efd, connfd);
                                }
                                continue;                        
                        }
                        else
                        {
                                inform_cb(&job[idx], events[i].data.fd);
                                //epoll_ctl(*efd, EPOLL_CTL_DEL, events[i].data.fd, ev);
                        }                
                }
        }
        return 0;        
}

#elif (defined __FreeBSD__) || (defined __APPLE__)

static int do_accept(int kq, int listenfd, int nfd)
{
        int i;
        int connfd = 0;
        errno = 0;

        for(i = 0; i < nfd; i++)
        {
                if((connfd = accept(listenfd, NULL, NULL)) == -1)
                        break;
                if(ev_register(kq, connfd) == -1)
                        return -1;
        }
        return 0;
}

int ev_run(int *breakall, int listenfd, int *efd, Event *events,
              int n, struct task *job, cb inform_cb)
{
        int nfd, i, idx = 0;

        for(;*breakall == 0;)
        {
                nfd = kevent(*efd, NULL, 0, events, MAX_EVENTS, NULL);

                for(i = 0; i < nfd; i++)
                {
                        assign_helper(&idx, n, job);

                        if(events[i].flags & EV_ERROR)
                        {
                                close(events[i].ident);
                                continue;
                        }
                        else if(listenfd == events[i].ident)
                        {
                                do_accept(*efd, listenfd, events[i].data);
                                if(errno)
                                        perror("do_accept");
                        }
                        else
                        {
                                inform_cb(&job[idx], events[i].ident);
                                //write(events[i].ident, "back", 5);
                        }
                }
        }
        return 0;
}

#endif

void ev_done(int *efd, Event *events)
{
        int i =0;
        for(; i < MAX_EVENTS; i++)      // ignore errors
        {
#ifdef __linux__
                //epoll_ctl(*efd, EPOLL_CTL_DEL, events[i].data.fd, ev);
                close(events[i].data.fd);
#else
                close(events[i].ident);
#endif
        }
        close(*efd);
        free(events);
}
